﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

namespace EncryptDecrypt
{
    public class SymmetricKeyHelper
    {
        const int AzureRowKeyMaxValue = 10000;

        X509Certificate2 cert = null;
        SymmetricKeyDataServiceContext ctx = null;

        public SymmetricKeyHelper(string baseAddress, Microsoft.WindowsAzure.StorageCredentials credentials, X509Certificate2 theCert)
        {
            cert = theCert;

            if (credentials != null)
                ctx = new SymmetricKeyDataServiceContext(baseAddress, credentials);
        }

    
        public SymmetricKey LoadSymmetricKey(string TableName, int? Version)
        {

              var query = (from n in ctx.SymmetricKeys
                      where n.PartitionKey == "P" + cert.Thumbprint  
                      select n).FirstOrDefault();

              SymmetricKey sm = query; // ctx.GetSymmetricKey(TableName, cert.Thumbprint, Version.Value);
            
            if (sm != null)
            {
                // If the key was found, decrypt it using the certificate then return
                sm.Key = DecryptSymmetricKey_RSA(sm, cert);
                return sm;
            }


            if (sm == null && Version.HasValue == false)
            {
                // If no keys were found then create a new one
                return CreateNewAESSymmetricKeyset();
            }
            else
            {
                // Specific Key not found, NOT creating a new one.
                return null;
            }
        }

        SymmetricKey CreateNewAESSymmetricKeyset()
        {
            return CreateNewAESSymmetricKeyset(32, 16);
        }

        internal SymmetricKey TestRSA()
        {
            SymmetricKey sm = CreateNewAESSymmetricKeyset();
            return sm;
        }
        /// <summary>
        /// Creates a symmetric key.  See this link for more information behind the numbers
        /// http://blogs.msdn.com/b/shawnfa/archive/2006/10/09/the-differences-between-rijndael-and-aes.aspx
        /// </summary>
        /// <returns></returns>
        SymmetricKey CreateNewAESSymmetricKeyset(int KeyLength, int ivLength)
        {
            if (cert == null)
                throw new InvalidOperationException("Unable to create new AES keyset; Certificate not loaded.");

            // 32 bytes (32 bytes * 8 bits in a byte == 256 bits)
            byte[] symmKey = CreateCryptograhicKey(KeyLength);

            // IV: 16 bytes (16 bytes * 8 bits in a byte == 128 bits) == 128 bit block size.
            byte[] iv = CreateCryptograhicKey(ivLength);

            SymmetricKey symmKeySet = new SymmetricKey() { iv = iv, Key = symmKey };
            symmKeySet.CertificateHash = cert.Thumbprint;
            symmKeySet.Version = 0;
            
            // Encrypt the Symmetric Key for storage
            symmKeySet.Key = EncryptSymmetricKey_RSA(symmKeySet, cert);

            // Determine the value of the most recent row
            var query = (from n in ctx.SymmetricKeys
                         where n.PartitionKey == "P" + cert.Thumbprint
                         select n).FirstOrDefault();

            int tempRowID = 0;
            if (query == null)
            {
                tempRowID = 0;
                symmKeySet.Version = 0;
            }
            else
            {
                tempRowID = query.Version + 1;
                symmKeySet.Version = query.Version + 1;
            }

            // Save to AzureTable
            symmKeySet.PartitionKey = "P"+cert.Thumbprint;
            int calculatedRowValue =  AzureRowKeyMaxValue - tempRowID;
            symmKeySet.RowKey = "R" + calculatedRowValue;
            ctx.SaveSymmetricKey(symmKeySet);

            // Cleanup
            // Return unencrypted value for key
            symmKeySet.Key = DecryptSymmetricKey_RSA (symmKeySet, cert);

            return symmKeySet;
        }


        byte[] CreateCryptograhicKey(int length)
        {
            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                byte[] key = new byte[length];

                crypto.GetBytes(key);

                return key;
            }
        }

        byte[] DecryptSymmetricKey_RSA(SymmetricKey encryptedValue, X509Certificate2 cert)
        {
            using (RSACryptoServiceProvider RSA = (RSACryptoServiceProvider)cert.PrivateKey)
            {
                byte[] decrypt = RSA.Decrypt(encryptedValue.Key, false);
                return decrypt;
            }
        }

        byte[] EncryptSymmetricKey_RSA(SymmetricKey symmKey, X509Certificate2 cert)
        {
            using (RSACryptoServiceProvider RSA = (RSACryptoServiceProvider)cert.PublicKey.Key)
            {
                var encrypt = RSA.Encrypt(symmKey.Key, false);
                return encrypt;
            }
        }

   
    }
}
